<?php
/**
 * #Celebrate Shieh
 *             ,%%%%%%%%,
 *           ,%%/\%%%%/\%%
 *          ,%%%\c "" J/%%%
 * %.       %%%%/ o  o \%%%
 * `%%.     %%%%    _  |%%%
 *  `%%     `%%%%(__Y__)%%'
 *  //       ;%%%%`\-/%%%'
 * ((       /  `%%%%%%%'
 *  \\    .'          |
 *   \\  /       \  | |
 *    \\/         ) | |
 *     \         /_ | |__
 *     (___________))))))) 攻城湿
 *
 *        _       _
 * __   _(_)_   _(_) __ _ _ __
 * \ \ / / \ \ / / |/ _` |'_ \
 *  \ V /| |\ V /| | (_| | | | |
 *   \_/ |_| \_/ |_|\__,_|_| |_|
 */
namespace App\Libs\Email;


use App\Libs\Storage\StorageFile;

class EmailImap
{


    /**
     * @var resource $_connect
     */
    private $_connect;
    /**
     * @var object $_mailInfo
     */
    private $_mailInfo;
    /**
     * @var int $_totalCount
     */
    private $_totalCount;
    /**
     * @var array $_totalCount
     */
    private $_contentType;

    /**
     * __construct of the class
     */
    function __construct()
    {
        $this->_contentType = [
            'ez'      => 'application/andrew-inset',
            'hqx'     => 'application/mac-binhex40',
            'cpt'     => 'application/mac-compactpro',
            'doc'     => 'application/msword',
            'bin'     => 'application/octet-stream',
            'dms'     => 'application/octet-stream',
            'lha'     => 'application/octet-stream',
            'lzh'     => 'application/octet-stream',
            'exe'     => 'application/octet-stream',
            'class'   => 'application/octet-stream',
            'so'      => 'application/octet-stream',
            'dll'     => 'application/octet-stream',
            'oda'     => 'application/oda',
            'pdf'     => 'application/pdf',
            'ai'      => 'application/postscript',
            'eps'     => 'application/postscript',
            'ps'      => 'application/postscript',
            'smi'     => 'application/smil',
            'smil'    => 'application/smil',
            'mif'     => 'application/vnd.mif',
            'xls'     => 'application/vnd.ms-excel',
            'ppt'     => 'application/vnd.ms-powerpoint',
            'wbxml'   => 'application/vnd.wap.wbxml',
            'wmlc'    => 'application/vnd.wap.wmlc',
            'wmlsc'   => 'application/vnd.wap.wmlscriptc',
            'bcpio'   => 'application/x-bcpio',
            'vcd'     => 'application/x-cdlink',
            'pgn'     => 'application/x-chess-pgn',
            'cpio'    => 'application/x-cpio',
            'csh'     => 'application/x-csh',
            'dcr'     => 'application/x-director',
            'dir'     => 'application/x-director',
            'dxr'     => 'application/x-director',
            'dvi'     => 'application/x-dvi',
            'spl'     => 'application/x-futuresplash',
            'gtar'    => 'application/x-gtar',
            'hdf'     => 'application/x-hdf',
            'js'      => 'application/x-javascript',
            'skp'     => 'application/x-koan',
            'skd'     => 'application/x-koan',
            'skt'     => 'application/x-koan',
            'skm'     => 'application/x-koan',
            'latex'   => 'application/x-latex',
            'nc'      => 'application/x-netcdf',
            'cdf'     => 'application/x-netcdf',
            'sh'      => 'application/x-sh',
            'shar'    => 'application/x-shar',
            'swf'     => 'application/x-shockwave-flash',
            'sit'     => 'application/x-stuffit',
            'sv4cpio' => 'application/x-sv4cpio',
            'sv4crc'  => 'application/x-sv4crc',
            'tar'     => 'application/x-tar',
            'tcl'     => 'application/x-tcl',
            'tex'     => 'application/x-tex',
            'texinfo' => 'application/x-texinfo',
            'texi'    => 'application/x-texinfo',
            't'       => 'application/x-troff',
            'tr'      => 'application/x-troff',
            'roff'    => 'application/x-troff',
            'man'     => 'application/x-troff-man',
            'me'      => 'application/x-troff-me',
            'ms'      => 'application/x-troff-ms',
            'ustar'   => 'application/x-ustar',
            'src'     => 'application/x-wais-source',
            'xhtml'   => 'application/xhtml+xml',
            'xht'     => 'application/xhtml+xml',
            'zip'     => 'application/zip',
            'au'      => 'audio/basic',
            'snd'     => 'audio/basic',
            'mid'     => 'audio/midi',
            'midi'    => 'audio/midi',
            'kar'     => 'audio/midi',
            'mpga'    => 'audio/mpeg',
            'mp2'     => 'audio/mpeg',
            'mp3'     => 'audio/mpeg',
            'aif'     => 'audio/x-aiff',
            'aiff'    => 'audio/x-aiff',
            'aifc'    => 'audio/x-aiff',
            'm3u'     => 'audio/x-mpegurl',
            'ram'     => 'audio/x-pn-realaudio',
            'rm'      => 'audio/x-pn-realaudio',
            'rpm'     => 'audio/x-pn-realaudio-plugin',
            'ra'      => 'audio/x-realaudio',
            'wav'     => 'audio/x-wav',
            'pdb'     => 'chemical/x-pdb',
            'xyz'     => 'chemical/x-xyz',
            'bmp'     => 'image/bmp',
            'gif'     => 'image/gif',
            'ief'     => 'image/ief',
            'jpeg'    => 'image/jpeg',
            'jpg'     => 'image/jpeg',
            'jpe'     => 'image/jpeg',
            'png'     => 'image/png',
            'tiff'    => 'image/tiff',
            'tif'     => 'image/tiff',
            'djvu'    => 'image/vnd.djvu',
            'djv'     => 'image/vnd.djvu',
            'wbmp'    => 'image/vnd.wap.wbmp',
            'ras'     => 'image/x-cmu-raster',
            'pnm'     => 'image/x-portable-anymap',
            'pbm'     => 'image/x-portable-bitmap',
            'pgm'     => 'image/x-portable-graymap',
            'ppm'     => 'image/x-portable-pixmap',
            'rgb'     => 'image/x-rgb',
            'xbm'     => 'image/x-xbitmap',
            'xpm'     => 'image/x-xpixmap',
            'xwd'     => 'image/x-xwindowdump',
            'igs'     => 'model/iges',
            'iges'    => 'model/iges',
            'msh'     => 'model/mesh',
            'mesh'    => 'model/mesh',
            'silo'    => 'model/mesh',
            'wrl'     => 'model/vrml',
            'vrml'    => 'model/vrml',
            'css'     => 'text/css',
            'html'    => 'text/html',
            'htm'     => 'text/html',
            'asc'     => 'text/plain',
            'txt'     => 'text/plain',
            'rtx'     => 'text/richtext',
            'rtf'     => 'text/rtf',
            'sgml'    => 'text/sgml',
            'sgm'     => 'text/sgml',
            'tsv'     => 'text/tab-separated-values',
            'wml'     => 'text/vnd.wap.wml',
            'wmls'    => 'text/vnd.wap.wmlscript',
            'etx'     => 'text/x-setext',
            'xsl'     => 'text/xml',
            'xml'     => 'text/xml',
            'mpeg'    => 'video/mpeg',
            'mpg'     => 'video/mpeg',
            'mpe'     => 'video/mpeg',
            'qt'      => 'video/quicktime',
            'mov'     => 'video/quicktime',
            'mxu'     => 'video/vnd.mpegurl',
            'avi'     => 'video/x-msvideo',
            'movie'   => 'video/x-sgi-movie',
            'ice'     => 'x-conference/x-cooltalk',
            'rar'     => 'application/x-rar-compressed',
            '*'       => 'application/octet-stream',
            'docx'    => 'application/msword',
        ];
    }

    /**
     * Open an IMAP stream to a mailbox
     *
     * @param string $host 类型
     * @param string $port 端口
     * @param string $user 用户
     * @param string $pass 密码
     *                     初始化连接
     *
     * @return resource|bool
     */
    function mailConnect( $user, $pass, $host = 'imap.qq.com', $port = '143' )
    {
        $this->_connect = imap_open("{" . $host . ':' . $port . "}INBOX", $user, $pass);//连接拼接
        if ( ! $this->_connect ) {
            errorStorage('email', '"Lcannot connect" : ' . imap_last_error(), '邮箱', 'error');
            return FALSE;
        }
        return $this->_connect;
    }

    /**
     * get the total count of the current mailbox
     *获取邮件总数
     *
     * @return int
     */
    function mailTotalCount()
    {
        $check             = imap_check($this->_connect);
        $this->_totalCount = $check->Nmsgs;
        return $this->_totalCount;
    }

    /**
     * Read the header of the message
     *
     * @param string $msgCount
     *获取邮件标题
     *
     * @return array
     */
    function mailHeader( $msgCount )
    {
        $mailHeader = [];
        $header     = imap_header($this->_connect, $msgCount);//获取
        $sender     = $header->from[ 0 ];
        $replyTo    = $header->reply_to[ 0 ];
        if ( strtolower($sender->mailbox) != 'mailer-daemon' && strtolower($sender->mailbox) != 'postmaster' ) $mailHeader = [
            'from'        => strtolower($sender->mailbox) . '@' . $sender->host,
            'fromName'    => $this->subjectDecode($sender->personal),
            'toOther'     => $this->subjectDecode(strtolower($replyTo->mailbox) . '@' . $replyTo->host),
            'toOtherName' => $this->subjectDecode($replyTo->personal),
            'subject'     => $this->subjectDecode($header->subject),
            'to'          => strtolower($header->toaddress),
            'date'        => $header->date,
            'id'          => $header->Msgno,
            'seen'        => $header->Unseen,
        ];//解析
        return $mailHeader;
    }

    /**
     * decode the subject of chinese
     *
     * @param string $subject
     *解析标题
     *
     * @return sting
     */
    function subjectDecode( $subject )
    {
        $beginStr = substr($subject, 0, 5);
        if ( $beginStr == '=?ISO' ) {
            $separator  = '=?ISO-2022-JP';
            $toEncoding = 'ISO-2022-JP';
        }
        else {
            $separator  = '=?GBK';
            $toEncoding = 'GBK';
        }
        $encode = strstr($subject, $separator);
        if ( $encode ) {
            $explodeArr = explode($separator, $subject);
            $length     = count($explodeArr);
            $subjectArr = [];
            for ( $i = 0; $i < $length / 2; $i++ ) {
                $subjectArr[ $i ][] = $explodeArr[ $i * 2 ];
                if ( @$explodeArr[ $i * 2 + 1 ] ) $subjectArr[ $i ][] = $explodeArr[ $i * 2 + 1 ];
            }
            foreach ( $subjectArr as $arr ) {
                $subSubject = implode($separator, $arr);
                if ( count($arr) == 1 ) $subSubject = $separator . $subSubject;
                $begin    = strpos($subSubject, "=?");
                $end      = strpos($subSubject, "?=");
                $beginStr = '';
                $endStr   = '';
                if ( $end > 0 ) {
                    if ( $begin > 0 ) $beginStr = substr($subSubject, 0, $begin);
                    if ( ( strlen($subSubject) - $end ) > 2 ) $endStr = substr($subSubject, $end + 2, strlen($subSubject) - $end - 2);
                    $str             = substr($subSubject, 0, $end - strlen($subSubject));
                    $pos             = strrpos($str, "?");
                    $str             = substr($str, $pos + 1, strlen($str) - $pos);
                    $subSubject      = $beginStr . imap_base64($str) . $endStr;
                    $subSubjectArr[] = iconv($toEncoding, 'utf-8', $subSubject);
                    mb_convert_encoding($subSubject, 'utf-8', 'gb2312,ISO-2022-JP');
                }
            }
            $subject = implode('', $subSubjectArr);
        }
        return $subject;
    }

    /**
     * Mark a message for deletion from current mailbox
     *删除邮件
     *
     * @param string $msgCount
     */
    function mailDelete( $msgCount )
    {
        imap_delete($this->_connect, $msgCount);
    }

    /**
     * get attach of the message
     *
     * @param string $msgCount
     * @param string $path 附件保存路径
     *                     获取邮件附件解析
     *
     * @return array
     */
    function getAttach( $msgCount, $path )
    {
        $struckture = imap_fetchstructure($this->_connect, $msgCount);
        $attach     = [];
        if ( isset($struckture->parts) ) {
            foreach ( $struckture->parts as $key => $value ) {
                $fileName = '';
                if ( ( isset($value->disposition) && $value->disposition == 'attachment' ) || strtolower($value->subtype) == 'rfc822_missing_envelope' ) {
                    if ( $key == 0 ) continue;
                    $fileName = NULL;
                    if ( strtolower($value->subtype) == 'octet-stream' ) $name = pathinfo($value->dparameters[ 0 ]->value, PATHINFO_EXTENSION) ?? 'txt';
                    else $name = getFileType(strtolower($value->subtype));
                    if ( $name ) $fileName = time() . GetRandStr(10) . '.' . $name;
                }
                $encoding = $struckture->parts[ $key ]->encoding;
                $message  = imap_fetchbody($this->_connect, $msgCount, $key + 1);
                switch ( $encoding ) {
                    case 0:
                        $message = imap_8bit($message);
                        break;
                    case 1:
                        $message = imap_8bit($message);
                        break;
                    case 2:
                        $message = imap_binary($message);
                        break;
                    case 3:
                        $message = imap_base64($message);
                        break;
                    case 4:
                        $message = quoted_printable_decode($message);
                        break;
                }
                if ( $fileName ) {
                    $this->downAttach($path, $fileName, $message);
                    $attach[] = $fileName;
                };
            }
        }
        return $attach;
    }

    /**
     * download the attach of the mail to localhost
     *保存在本地
     *
     * @param string $filePath
     * @param string $message 内容
     * @param string $name    文件名
     */
    function downAttach( $filePath, $name, $message )
    {
        $disk = new StorageFile();
        $ext  = pathinfo($name, PATHINFO_EXTENSION);
        $disk->makeDirectory($filePath . $ext);
        $disk->put($filePath . $ext . '/' . $name, $message);
    }

    /**
     * get the body of the message
     *
     * @param string $msgCount
     *邮件内容
     *
     * @return string
     */
    function getBody( $msgCount )
    {
        $body = $this->getPart($msgCount, "TEXT/HTML");
        if ( $body == '' ) $body = $this->getPart($msgCount, "TEXT/PLAIN");
        if ( $body == '' ) return '';
        return $body;
    }

    /**
     * Read the structure of a particular message and fetch a particular
     * section of the body of the message
     *
     * @param string $msgCount
     * @param string $mimeType
     * @param object $structure
     * @param string $partNumber
     *邮件内容解析
     *
     * @return string|bool
     */
    private function getPart( $msgCount, $mimeType, $structure = FALSE, $partNumber = FALSE )
    {
        if ( ! $structure ) $structure = imap_fetchstructure($this->_connect, $msgCount);
        if ( $structure ) {
            if ( $mimeType == $this->getMimeType($structure) ) {
                if ( ! $partNumber ) $partNumber = "1";
                $fromEncoding = $structure->parameters[ 0 ]->value;
                $text         = imap_fetchbody($this->_connect, $msgCount, $partNumber);
                if ( $structure->encoding == 3 ) $text = imap_base64($text);
                else if ( $structure->encoding == 4 ) $text = imap_qprint($text);
                $text = mb_convert_encoding($text, 'utf-8', $fromEncoding);
                return $text;
            }
            if ( $structure->type == 1 ) {
                while ( list($index, $subStructure) = each($structure->parts) ) {
                    $prefix = '';
                    if ( $partNumber ) $prefix = $partNumber . '.';
                    $data = $this->getPart($msgCount, $mimeType, $subStructure, $prefix . ( $index + 1 ));
                    if ( $data ) return $data;
                }
            }
        }
        return FALSE;
    }

    /**
     * get the subtype and type of the message structure
     *获取邮件类型
     *
     * @param object $structure
     */
    private function getMimeType( $structure )
    {
        $mimeType = [ "TEXT", "MULTIPART", "MESSAGE", "APPLICATION", "AUDIO", "IMAGE", "VIDEO", "OTHER" ];
        if ( $structure->subtype ) return $mimeType[ (int) $structure->type ] . '/' . $structure->subtype;
        return "TEXT/PLAIN";
    }

    /**
     * put the message from unread to read
     *
     * @param string $msgCount
     *
     * @return bool
     */
    function mailRead( $msgCount )
    {
        $status = imap_setflag_full($this->_connect, $msgCount, "\\Seen");
        return $status;
    }

    /**
     * Close an IMAP stream
     * 关闭
     */
    function closeMail()
    {
        imap_close($this->_connect, CL_EXPUNGE);
    }
}
